home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PsL Monthly 1993 December
/
PSL Monthly Shareware CD-ROM (December 1993).iso
/
prgmming
/
dos
/
pascal
/
mrkrls.exe
/
MRKRLS.PAS
< prev
Wrap
Pascal/Delphi Source File
|
1993-02-19
|
14KB
|
403 lines
{
like many others, I am sure, I had the need to port a real-mode
program to protected mode, and all memory management was done via
MARK-RELEASE. As we all know, this is not supported in protected
mode, and re-writting a large program to use getmem-freemem is
practically impossible. Therefore, I came up with this piece of
code to simulate the operation of mark-release. I have tested it
on my program , and it seems to work. However, I had to make a
lot of assumptions as to how the new memory management works,
without any real documentation. Therefore, I dont trust this code
much. The purpose of uploading it here is threefold:
1. I would like to get comments on the code and my rationale from
people that really understand protected mode
2. I would like the code to be tested with more programs than I
could ever write!
3. I want others with the same problem to benefit from it.
Therefore, please feel free to use this code with your programs,
no royalties asked. BUT there is one thing I DO ask , and it is
this: PLEASE let me know if the code works for you or not, and
any problems that you may encounter with it. This is a
developer's forum, and we would all benefit from any
comments-problems you would report. I am certain there is a
million or programs out there just DYING to port to protected
mode, that are facing the same problem. BE SOCIAL. Share your
opinions with others.
The preferred method of contact is E-MAIL, or messages posted in
BPASCAL, section 6, protected mode.
Mike Cariotoglou,
CIS ID : 100012,1767
DOCUMENTATION (sort of..)
-------------------------
The protected mode memory manager works somewhat like this :
The system maintains two variables, HEAPLIMIT and HEAPBLOCK
Any request for memory >=HEAPLIMIT gets a "normal" allocation ,
ie a selector-offset pointer that is managed by the RTM. Any
request smaller than this, is handled as follows:
The BP heap manager allocates a "Large" memory block, size
HEAPBLOCK, and then "carves" or sub-allocates this to smaller
chunks that it passes to the application. Each large block
maintains its own freelist of deallocated pointers, and manages
free "small" blocks very much like T6 memory manager.
(By large blocks I mean blocks >=HEAPLIMIT, that are not
sub-allocated. By the way, a program can distinguish between a
large and small block by the fact that the offset of the
corresponding pointer is 0 for a large block, and >=12 for a
small block).
It follows from this, that each large block behaves like a T6
heap, along with the 8-byte granularity used by the T6 manager to
reserve space for the free list entries. However, I believe that
here the granularity is 4 bytes, not 8, since only WORDS are
needed to manipulate a heap of <=64k. This is not important, and
would affect only programs that assumed and used this 8-byte
granularity. I am sure, however, that such programs would be
bound to do pointer arithmetic also, meaning they would break in
many more places than this.
Each large block has an overhead of 12 bytes, which are used to
maintain the block and link to other large blocks.
The manual says that the default for heaplimit and heapblock are
1024 and 8192, and suggests that heapblock is at least
4xHeaplimit. I dont really see any reason for that, so I
experimented with the following values :
HEAPBLOCK=65532 ( I believe this has to be a multiple of 4)
HEAPLIMIT=heapblock-12; (the overhead)
This gives you a large block that is similar to a 64k T6 heap,
and its performance speedwise is very acceptable, so these are
the values I used. It is important that heaplimit is as large as
possible, since any programs using the technique I will describe
CANNOT allocate a block of more than HEAPLIMIT bytes. The above
values give a maximum block of 65520, which happens to be the
limit in the old T6 heap manager as well.
Each "small heap" of 64k belongs to a circular linked list. The
system variable HEAPLIST holds a selector that can be used as an
entry point to the circular list. This selector does NOT point to
the head of the list (the first large block allocated), but
rather at the most recently used "small" heap. I believe this is
done to improve performance, since the heap manager traverses
this list when asked for memory, and this ensures that the most
recently used block will be used.
Now, the basic idea behind my MARK-RELEASE implemenation:
Each time MARK is called, the current HEAPLIST variable is saved
in a stack, and then it set to 0. This has the effect of taking
any memory allocated before the MARK "off-line", making it
unaccesible to the heap manager. The first allocation after the
MARK will create a new 64k "small" heap, that will be managed by
the heap manager normally. When this is exausted, a new one will
be allocated, and so on. When RELEASE is called it will :
a. de-allocate all 64k heaps that belong to the current HEAPLIST
b. de-allocate all heaplists created up to but not including the
corresponding MARK
c. restore the heaplist saved when MARK was called.
This has the effect of restoring the memory state exactly as it
was when the MARK was called. Note that step (b) above allows us
to use MARK-RELEASE in nested form, and RELEASE to any point
MARKED. This often happens in real programs. An example:
mark(p1);
...
...
...
mark(p2);
...
...
release(p1);
This would work with the old heap manager, and it will also work
here, being the equivalent of :
mark(p1);
...
...
...
mark(p2);
...
...
release(p2);
release(p1);
(actually, this system works BETTER than the old, since any
memory in the freelist before the MARK is not lost, as was the
case with the old manager, which always cleared the free list
upon a RELEASE)
This is the basic idea, the comments in the code should clarify
more.
Limitations and DONT'S
----------------------
1. All allocations requested by the application MUST be
less than HEAPLIMIT, so that they get sub-allocated. If any block
>= heaplimit is allocated, this memory will NOT belong to a small
heap, therefore will NOT be de-allocated by the release, and will
be PERMANENTLY lost. My GETMEM dummy ensures this ,by halting
the program in such a case. However, I cannot trap calls to NEW
like that, and therefore the potential for loss of memory exists.
I do not expect many programs to be bothered by it, since the
old real mode heap manager would NOT allow an allocation of more
than 65520 anyway, so existing programs should NEVER create this
situation. Still,I thought best to mention it.
2. Do not expect multiple allocations to give you consecutive
memory space. This was also the case with T6, therefore, again, a
program should not rely on this. (it would involve pointer math,
anyway, which is a DONT in protected mode)
3. It is best that nested MARK-RELEASE calls are matched. Failing
this, it is acceptable (as shown above) to do a RELEASE with a
pointer that was MARKED before the last mark (any level of
nesting). What should NOT be done, is to leave dangling MARKS
around, that are not cleared by a RELEASE with this or a previous
MARK. This would lead the program to die soon, since the internal
stack of UHEAP would overflow. In other words:
mark(p1)
...
release(p1)
is best
OR
mark(p1)
...
mark(p2)
...
mark(p3)
...
release(p1)
is OK
BUT
mark(p1)
...
mark(p2)
...
release(p2);
(missing RELEASE(p1))
is NOT allowed.
Unfortunately, this was allowed in the old heap manager, since
MARK did not do anything significant, (what it actually was, was
mark(p) :: p:=heapptr;
Therefore, a situation like this MAY exist in old programs.
you will find out by the fact that the program will crash with a
runtime error if such a thing exists. A good way to test for this
would be to take the program th